using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using UvsChess;

namespace StudentAI
{
    public class StudentAI : IChessAI
    {
        #region IChessAI Members that are implemented by the Student

        /// <summary>
        /// The name of your AI
        /// </summary>
        public string Name
        {
#if DEBUG
            get { return "Andrew & Mike's AI (Debug)"; }
#else
            get { return "Andrew & Mike's AI"; }
#endif
        }

        static public StudentAI studentAI;
        static public Stopwatch stopwatch = new Stopwatch();

        /// <summary>
        /// Evaluates the chess board and decided which move to make. This is the main method of the AI.
        /// The framework will call this method when it's your turn.
        /// </summary>
        /// <param name="board">Current chess board</param>
        /// <param name="yourColor">Your color</param>
        /// <returns> Returns the best chess move the player has for the given chess board</returns>
        public ChessMove GetNextMove(ChessBoard board, ChessColor myColor)
        {
            StudentAI.studentAI = this;
            stopwatch.Reset();
            stopwatch.Start();

            Heuristics heuristic = new Heuristics();
            ChessMove nextMove = heuristic.minimax(board,myColor,heuristic.MikesSumScore, 4);
            
            if (nextMove == null)
            {
                nextMove = new ChessMove(new ChessLocation(0, 0), new ChessLocation(0, 0));
                nextMove.Flag = ChessFlag.Stalemate;
                return nextMove;
            }

            //Check for Check
            ChessBoard nextBoard = new ChessBoard(board.ToPartialFenBoard());
            nextBoard.MakeMove(nextMove);
            ChessColor enemyColor = ChessUtils.getOppositeColor(myColor);
            ChessLocation enemyKing = MoveValidation.findKing(nextBoard, enemyColor);
            List<ChessMove> moves = MoveGeneration.getAllMoves(nextBoard, enemyColor);

            if (moves.Count == 0 && MoveValidation.kingInCheck(nextBoard, enemyKing, enemyColor))
            {   
                nextMove.Flag = ChessFlag.Checkmate;
                Heuristics.IN_CHECKMATE = false;
            }
            //if (Heuristics.IN_CHECKMATE)
           //{
            //    nextMove.Flag = ChessFlag.Checkmate;
            //    Heuristics.IN_CHECKMATE = false;
           // }
            else if (MoveValidation.kingInCheck(nextBoard, enemyKing, enemyColor)) {
                nextMove.Flag = ChessFlag.Check;
            }
            

            return nextMove;
        }

        /// <summary>
        /// Validates a move. The framework uses this to validate the opponents move.
        /// </summary>
        /// <param name="boardBeforeMove">The board as it currently is _before_ the move.</param>
        /// <param name="moveToCheck">This is the move that needs to be checked to see if it's valid.</param>
        /// <param name="colorOfPlayerMoving">This is the color of the player who's making the move.</param>
        /// <returns>Returns true if the move was valid</returns>
        public bool IsValidMove(ChessBoard boardBeforeMove, ChessMove moveToCheck, ChessColor colorOfPlayerMoving)
        {
            if( !MoveValidation.SameColor(boardBeforeMove[moveToCheck.From], colorOfPlayerMoving ) ) {
                return false;
            }

            bool validity = true;
            ChessLocation kingPosition = MoveValidation.findKing(boardBeforeMove,colorOfPlayerMoving);
            ChessPiece fromPiece = boardBeforeMove[moveToCheck.From.X, moveToCheck.From.Y];
            ChessPiece toPiece = boardBeforeMove[moveToCheck.To.X,moveToCheck.To.Y];
            ChessBoard newBoard = new ChessBoard(boardBeforeMove.ToPartialFenBoard());            
            newBoard.MakeMove(moveToCheck);
            
            //Check piece move validity
            switch (fromPiece) { 
                case ChessPiece.BlackBishop:
                case ChessPiece.WhiteBishop:
                    validity = MoveValidation.validateBishopMove(boardBeforeMove, moveToCheck, colorOfPlayerMoving, kingPosition);
                    break;
                case ChessPiece.BlackKing:
                case ChessPiece.WhiteKing:
                    validity = MoveValidation.validateKingMove(boardBeforeMove, moveToCheck, colorOfPlayerMoving, kingPosition);
                    break;
                case ChessPiece.BlackKnight:
                case ChessPiece.WhiteKnight:
                    validity = MoveValidation.validateKnightMove(boardBeforeMove, moveToCheck, colorOfPlayerMoving, kingPosition);
                    break;
                case ChessPiece.BlackPawn:
                case ChessPiece.WhitePawn:
                    validity = MoveValidation.validatePawnMove(boardBeforeMove, moveToCheck, colorOfPlayerMoving, kingPosition);
                    break;
                case ChessPiece.BlackQueen:
                case ChessPiece.WhiteQueen:
                    validity = MoveValidation.validateQueenMove(boardBeforeMove, moveToCheck, colorOfPlayerMoving, kingPosition);
                    break;
                case ChessPiece.BlackRook:
                case ChessPiece.WhiteRook:
                    validity = MoveValidation.validateRookMove(boardBeforeMove, moveToCheck, colorOfPlayerMoving, kingPosition);
                    break;
                case ChessPiece.Empty:
                    validity = false;
                    break;
                default:
                    break;
            }

            kingPosition = MoveValidation.findKing(newBoard, colorOfPlayerMoving);

            if (MoveValidation.kingInCheck(newBoard, kingPosition, colorOfPlayerMoving))
            {
                return false;
            }

            switch (moveToCheck.Flag) { 
                case ChessFlag.Check:
                    if (!MoveValidation.kingInCheck(newBoard, MoveValidation.findKing(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving)), ChessUtils.getOppositeColor(colorOfPlayerMoving)))
                        validity = false;
                    List<ChessMove> moves = MoveGeneration.getAllMoves(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving));
                    if (moves.Count == 0)
                        validity = false;
                    break;
                case ChessFlag.Checkmate:
                    if (!MoveValidation.kingInCheck(newBoard, MoveValidation.findKing(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving)), ChessUtils.getOppositeColor(colorOfPlayerMoving)))
                    {
                        validity = false;
                        break;
                    }
                    moves = MoveGeneration.getAllMoves(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving));
                    if (moves.Count != 0)
                        validity = false;
                    break;
                case ChessFlag.NoFlag:
                    if (MoveValidation.kingInCheck(newBoard, MoveValidation.findKing(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving)), ChessUtils.getOppositeColor(colorOfPlayerMoving)))
                    {
                        validity = false;
                        break;
                    }
                    if (MoveGeneration.getAllMoves(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving)).Count == 0)
                        validity = false;
                    break;
                case ChessFlag.Stalemate:
                    if (MoveValidation.kingInCheck(newBoard, MoveValidation.findKing(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving)), ChessUtils.getOppositeColor(colorOfPlayerMoving)))
                    {
                        validity = false;
                        break;
                    }
                    if (MoveGeneration.getAllMoves(newBoard, ChessUtils.getOppositeColor(colorOfPlayerMoving)).Count != 0)
                        validity = false;
                    break;
            }

            return validity;
        }

        #endregion



        #region IChessAI Members that should be implemented as automatic properties and should NEVER be touched by students.
        /// <summary>
        /// This will return false when the framework starts running your AI. When the AI's time has run out,
        /// then this method will return true. Once this method returns true, your AI should return a 
        /// move immediately.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        public AIIsMyTurnOverCallback IsMyTurnOver { get; set; }

        /// <summary>
        /// Call this method to print out debug information. The framework subscribes to this event
        /// and will provide a log window for your debug messages.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="message"></param>
        public AILoggerCallback Log { get; set; }

        /// <summary>
        /// Call this method to catch profiling information. The framework subscribes to this event
        /// and will print out the profiling stats in your log window.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="key"></param>
        public AIProfiler Profiler { get; set; }

        /// <summary>
        /// Call this method to tell the framework what decision print out debug information. The framework subscribes to this event
        /// and will provide a debug window for your decision tree.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="message"></param>
        public AISetDecisionTreeCallback SetDecisionTree { get; set; }
        #endregion
    }
}
